﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Rockfan.Framework.Audio;
using Rockfan.Framework.Graphics;

namespace Rockfan.Framework.Entities
{
    public sealed class Guitarboard : Playerboard
    {
        public bool IsStarted { get { return IsBoardInPosition && AreButtonsInitialized; } }

        public bool IsEnded { get; private set; }

        #region Methods

        public Guitarboard(PlayerIndex index, Chart chart, AudioTrack track)
            : base(index, chart.GuitarTracks)
        {
            this.PlayerType = PlayerType.RemotePlayer;
            this.AudioTrack = track;
            this.BoardSpeed = 1.33f;   // Hyperspeed == Basespeed + .175 / factor. example. ExpertBaseSpeed = 1.33 + 5Hyper = 2.205
            this.EnableUpdates = true;
            this.EnableDrawing = true;
            this.ChartFile = chart;
            this.CurrentNoteTrack = chart.GuitarTracks.Expert;
            this.ButtonInitializationTimeRemaining = float.NaN;

            Notes = new List<PlayNote>();
            Buttons = new List<Fretbutton>();
            BeatMarkers = new List<Beatmarker>();
            SustainNotes = new List<Sustain>();
            Flames = new List<Fretbutton>();
            Hitglows = new List<AnimatedSprite>();

            CurrentBPM = 120.0f;
            CurrentTimeSigNumerator = 4;
            CurrentTimeSigDenominator = 4;

            EnablePlayer1Notes = false;
            EnablePlayer2Notes = false;

            if (AudioTrack == null)
                AudioDelay = 0;

            else AudioDelay = AudioTrack.Delay;

            if (BotRandom == null)
            {
                var ticks = TimeSpan.FromMilliseconds(new Random((int)index + 1).NextDouble() * 1000000000.0f).Ticks;
                BotRandom = new Random((int)(DateTime.Now.Ticks - ticks));
            }
        }

        public override void LoadContent()
        {
            SetupBoard();
            SetupCamera();
            SetupNoteContent();

            BeatMarkerTexture = new Texture2D(EngineServices.GraphicsDevice, 1, 1);
            BeatMarkerTexture.SetData<Color>(new Color[] { Color.White });
        }

        public override async Task LoadContentAsync(ProgressChanged callback)
        {
            // [TODO] Load content here.

            await Task.Run(() => { });
        }

        public override void Start()
        {
            FinalBoardPosition = 82.0f;
            Camera.XRotation = 126.0f;
            BoardPositionTimeRemaing = 0.825f;
            BoardRate = ((Camera.XRotation - FinalBoardPosition) / BoardPositionTimeRemaing) / 60.0f;
        }

        public override void Finish()
        {

        }

        public override void SwapNotes(float delta)
        {
            NotesSwapped = !NotesSwapped;

            foreach (var note in Notes)
                note.Swap(delta);

            foreach (var button in Buttons)
                button.Swap(delta);
        }

        public override void UpdateBoardPosition(float delta)
        {

        }

        public override void ChangeDifficulty(Difficulty difficulty)
        {

        }

        protected override void Update(float delta)
        {
            if (AudioTrack != null)
            {
                AudioEngine.Update(0.0f);
                Playspeed = AudioTrack.Playspeed;
                CurrentTime = AudioTrack.Playposition;
            }
            else
            {
                Playspeed = 100.0f;
                CurrentTime = 0.0f;
            }

            //var hitRange = (4.5f * 75.0f) / 1000.0f;
            var distance = (delta * 5.0f * BoardSpeed * (Playspeed / 100.0f));

            Fretboard.BottomTextureCoordinate += distance / 5.0f;
            Fretboard.TopTextureCoordinate = Fretboard.BottomTextureCoordinate + 2.0f;
            Fretboard.Update(delta, true);

            #region Add Events

            if (Chart != null)
            {
                #region Update Beats

                if (ChartFile.Beats.Count > LastBeatIndex && CurrentTime != 0)
                {
                    var tempoEvent = ChartFile.Beats[LastBeatIndex];

                    if (tempoEvent.Start <= CurrentTime * 1000.0f)
                    {
                        LastBeatIndex++;
                        CurrentBPM = tempoEvent.BPM;

                        var id = tempoEvent.ID;
                        var offset = (CurrentTime - (tempoEvent.Start / 1000.0f)) * (5.0f * BoardSpeed * (Playspeed / 100.0f));
                        var markerType = tempoEvent.ID;

                        if ((int)markerType > 0)
                            AddBeatMarker(markerType, offset);
                    }
                }

                #region Update TimeSig

                for (int i = LastTimeSigIndex; i < ChartFile.TimeSignatureTrack.Count; i++)
                {
                    var timeSig = ChartFile.TimeSignatureTrack[i];

                    if ((timeSig.Start / 1000.0f) <= CurrentTime)
                    {
                        LastTimeSigIndex = i;
                        CurrentTimeSigNumerator = timeSig.Numerator;
                        CurrentTimeSigDenominator = timeSig.Denominator;

                        if (LastTimeSigIndex + 1 == ChartFile.TimeSignatureTrack.Count)
                            LastTimeSigIndex++;
                    }
                    else
                    {
                        if (LastTimeSigIndex < i)
                            LastTimeSigIndex = i;

                        break;
                    }
                }

                #endregion

                #endregion

                #region Add Notes

                if (CurrentNoteTrack.Count > LastNoteIndex && CurrentTime != 0)
                {
                    var noteEvent = CurrentNoteTrack[LastNoteIndex];

                    while (noteEvent.Start < CurrentTime * 1000.0f)
                    {
                        LastNoteIndex++;

                        if (GetIsInPlayerSection(noteEvent.Start))
                        {
                            var id = noteEvent.ID;
                            var offset = (CurrentTime - (noteEvent.Start / 1000.0f)) * (5.0f * BoardSpeed * (Playspeed / 100.0f));

                            if (((int)id >= 2 && (int)id <= 32) || ((int)id >= 2 + (int)NoteType.Hopo && (int)id <= 32 + (int)NoteType.Hopo))
                            {
                                AddNote(id, offset);

                                if (noteEvent.Length > 0)
                                    AddSustain(id, noteEvent.Length / 500.0f, AudioDelay / (20.0f * ((AudioDelay * BoardSpeed) - AudioDelay + 1)), offset);

                                if (GetIsInPowerSection(noteEvent.Start) && PowerModeEnabled)
                                {
                                    var lastNote = Notes.Last();
                                    lastNote.NoteType |= NoteType.Power;
                                }

                                if (GetIsFirstNoteInPlayerSection(noteEvent))
                                {
                                    var lastNote = Notes.Last();

                                    if (lastNote.IsHopo)
                                    {
                                        lastNote.NoteType &= ~NoteType.Hopo;
                                        lastNote.UpdateState(0.0f);
                                    }
                                }
                            }
                        }

                        if (LastNoteIndex >= CurrentNoteTrack.Count)
                            break;

                        noteEvent = CurrentNoteTrack[LastNoteIndex];
                    }
                }

                #endregion
            }

            #endregion

            #region Update Markers

            for (int i = BeatMarkers.Count - 1; i >= 0; i--)
            {
                var marker = BeatMarkers[i];
                marker.Alpha = (5.0f - marker.Y) * 0.33f;

                if (marker.MarkerType == MarkerType.Eigth && marker.Alpha > .4f)
                    marker.Alpha = 0.4f;

                else if (marker.MarkerType == MarkerType.Beat && marker.Alpha > 0.75f)
                    marker.Alpha = 0.75f;

                if (marker.Y - distance > -5.5f)
                {
                    marker.Y -= distance;
                    marker.UpdateState(delta);
                }
                else BeatMarkers.RemoveAt(i);
            }

            #endregion

            #region Update Notes

            for (int i = Notes.Count - 1; i >= 0; i--)
            {
                var note = Notes[i];
                note.Alpha = (5.0f - note.Y) * 0.33f;

                if (note.Y - distance > -5.5f)
                {
                    note.Y -= distance;
                    note.UpdateState(delta);
                }
                else Notes.RemoveAt(i);
            }

            #endregion

            #region Update Sustains

            for (int i = SustainNotes.Count - 1; i >= 0; i--)
            {
                var sustain = SustainNotes[i];

                sustain.Alpha = (5.0f - sustain.Y + sustain.YScale) * 0.05f;

                if (sustain.TimeRemaining >= 0)
                {
                    if (sustain.Y + sustain.YScale >= -4.5f)
                    {
                        sustain.YScale += distance / 2.0f;
                        sustain.Y -= distance / 2.0f;
                    }
                    else sustain.Y = (-4.5f + 5.0f);
                }
                else sustain.Y -= distance;

                if (sustain.Y + sustain.YScale <= -5.5f && sustain.TimeRemaining <= 0)
                    SustainNotes.RemoveAt(i);

                else SustainNotes[i].UpdateState(delta);
            }

            #endregion

            #region UpdateFlames

            for (int i = Flames.Count - 1; i >= 0; i--)
            {
                if (Flames[i].YScale > 0.225f)
                    Flames.RemoveAt(i);

                else
                {
                    Flames[i].YScale += delta;
                    Flames[i].Alpha += delta * (1 / 0.225f);
                    Flames[i].Z = Flames[i].YScale;
                }
            }

            #endregion

            if (IsStarted)
            {
                if (PlayerType != Framework.PlayerType.Human)
                    UpdateBot(delta);

                foreach (var note in Notes)
                    note.UpdateState(delta);

                foreach (var sustain in SustainNotes)
                    sustain.UpdateState(delta);

                foreach (var flame in Flames)
                    flame.UpdateState(delta);

                foreach (var hitGlow in Hitglows)
                    hitGlow.UpdateState(delta); 
            }
            else UpdateInitialization(delta);            

            foreach (var button in Buttons)
                button.UpdateState(delta);           
        }

        private void UpdateBot(float delta)
        {
            #region Check Sustains

            var hittingSustain = false;

            if (SustainNotes.Count > 0)
            {
                LastButtons = FretButtonType.None;

                foreach (var sustain in SustainNotes)
                {
                    if (LastNoteHit)
                    {
                        if (sustain.IsDead)
                            continue;

                        if (sustain.Y - sustain.YScale <= -4.5f)
                        {
                            if (sustain.NoteType.HasFlag(NoteType.Green))
                                LastButtons |= FretButtonType.Green;

                            if (sustain.NoteType.HasFlag(NoteType.Red))
                                LastButtons |= FretButtonType.Red;

                            if (sustain.NoteType.HasFlag(NoteType.Yellow))
                                LastButtons |= FretButtonType.Yellow;

                            if (sustain.NoteType.HasFlag(NoteType.Blue))
                                LastButtons |= FretButtonType.Blue;

                            if (sustain.NoteType.HasFlag(NoteType.Orange))
                                LastButtons |= FretButtonType.Orange;

                            if (LastNoteHit)
                                LastButtons |= FretButtonType.Hit;

                            EnableHitGlow(LastButtons);
                            PressButtons(LastButtons);
                            hittingSustain = true;
                        }
                        else break;
                    }
                }

                PressButtons(LastButtons);
            }

            #endregion

            #region Apply Long Range Key Presses

            var hitRange = (4.5f * 500.0f) / 1000.0f;

            if (hittingSustain)
                hitRange = (4.5f * 125.0f) / 1000.0f;

            var nextNoteSet = GetHitableNotes(hitRange);

            if (nextNoteSet.Count > 0)
            {
                if (!hittingSustain)
                    LastButtons = FretButtonType.None;

                foreach (var notes in nextNoteSet)
                {
                    if (notes[0].IsDead)
                        continue;

                    foreach (var note in notes)
                    {
                        if (note.NoteType.HasFlag(NoteType.Green))
                            LastButtons |= FretButtonType.Green;

                        if (note.NoteType.HasFlag(NoteType.Red))
                            LastButtons |= FretButtonType.Red;

                        if (note.NoteType.HasFlag(NoteType.Yellow))
                            LastButtons |= FretButtonType.Yellow;

                        if (note.NoteType.HasFlag(NoteType.Blue))
                            LastButtons |= FretButtonType.Blue;

                        if (note.NoteType.HasFlag(NoteType.Orange))
                            LastButtons |= FretButtonType.Orange;
                    }

                    foreach (var sustain in SustainNotes)
                    {
                        if (sustain.Y - sustain.YScale <= -4.55f)
                        {
                            sustain.NoteType |= NoteType.Dead;
                            EnableHitGlow(FretButtonType.None);
                        }
                    }                    
                    break;
                }

                PressButtons(LastButtons);
            }

            #endregion

            #region Apply Immediate Key Presses

            nextNoteSet = GetHitableNotes((4.5f * 25.0f) / 1000.0f);

            if (nextNoteSet.Count > 0)
            {
                var HitNext = (BotRandom.NextDouble() * 1000.0f) < (984 + (BotRandom.NextDouble() * BotRandom.Next(0, 64)));

                foreach (var notes in nextNoteSet)
                {
                    if (notes[0].IsDead)
                        continue;

                    foreach (var note in notes)
                    {
                        if (HitNext)
                        {
                            if (note.NoteType.HasFlag(NoteType.Green) && LastButtons.HasFlag(FretButtonType.Green))
                                AddFlame(FretButtonType.Green);

                            if (note.NoteType.HasFlag(NoteType.Red) && LastButtons.HasFlag(FretButtonType.Red))
                                AddFlame(FretButtonType.Red);

                            if (note.NoteType.HasFlag(NoteType.Yellow) && LastButtons.HasFlag(FretButtonType.Yellow))
                                AddFlame(FretButtonType.Yellow);

                            if (note.NoteType.HasFlag(NoteType.Blue) && LastButtons.HasFlag(FretButtonType.Blue))
                                AddFlame(FretButtonType.Blue);

                            if (note.NoteType.HasFlag(NoteType.Orange) && LastButtons.HasFlag(FretButtonType.Orange))
                                AddFlame(FretButtonType.Orange);

                            Notes.Remove(note);
                            LastNoteHit = true;
                            AudioTrack.Volume = 50.0f;
                        }

                        else
                        {
                            if (note.NoteType.HasFlag(NoteType.Power))
                                ResetPowerNotes();

                            note.NoteType = NoteType.Dead;
                            LastNoteHit = false;
                        }
                    }

                    if (!LastNoteHit)
                    {
                        LastButtons = FretButtonType.None;

                        foreach (var sustain in SustainNotes)
                        {
                            if (sustain.Y - sustain.YScale <= -4.0f)
                            {
                                sustain.NoteType = NoteType.Dead;

                                if (sustain.NoteType.HasFlag(NoteType.Green))
                                    LastButtons |= FretButtonType.Green;

                                if (sustain.NoteType.HasFlag(NoteType.Red))
                                    LastButtons |= FretButtonType.Red;

                                if (sustain.NoteType.HasFlag(NoteType.Yellow))
                                    LastButtons |= FretButtonType.Yellow;

                                if (sustain.NoteType.HasFlag(NoteType.Blue))
                                    LastButtons |= FretButtonType.Blue;

                                if (sustain.NoteType.HasFlag(NoteType.Orange))
                                    LastButtons |= FretButtonType.Orange;

                                EnableHitGlow(FretButtonType.None);
                            }
                        }

                        AudioTrack.Volume = 20.0f;
                    }                    

                    PressButtons(LastButtons);
                    break;
                }
            }
            

            #endregion
        }

        private void UpdateRemotePlayer(float delta)
        {
            #region Remote State Old

            //if (PlayerType == PlayerType.RemotePlayer)
            //{
            //    hitRange = (4.5f * 20.0f) / 1000.0f;
            //    var sustainPressed = false;
            //    var customHit = GetHitableNotes(hitRange);

            //    #region Check Notes

            //    for (int i = 0; i < customHit.Count; i++)
            //    {
            //        var notes = customHit[i];
            //        LastButtons = FretButtonType.None;

            //        if (notes[0].IsDead)
            //            continue;

            //        foreach (var note in notes)
            //        {
            //            if (HitNext == true)
            //            {
            //                if (note.NoteType.HasFlag(NoteType.Green))
            //                    AddFlame(FretButtonType.Green);

            //                if (note.NoteType.HasFlag(NoteType.Red))
            //                    AddFlame(FretButtonType.Red);

            //                if (note.NoteType.HasFlag(NoteType.Yellow))
            //                    AddFlame(FretButtonType.Yellow);

            //                if (note.NoteType.HasFlag(NoteType.Blue))
            //                    AddFlame(FretButtonType.Blue);

            //                if (note.NoteType.HasFlag(NoteType.Orange))
            //                    AddFlame(FretButtonType.Orange);

            //                Notes.Remove(note);
            //                AudioTrack.Volume = 50.0f;
            //            }
            //            else AudioTrack.Volume = 20.0f;

            //            foreach (var sustain in SustainNotes)
            //            {
            //                if (sustain.Y - sustain.YScale <= -4.5f)
            //                {
            //                    sustain.NoteType |= NoteType.Dead;
            //                    EnableHitGlow(FretButtonType.None);
            //                }
            //            }
            //        }

            //        PressButtons(LastButtons);
            //        break;
            //    }

            //    #endregion

            //    #region Check Sustains

            //    if (SustainNotes.Count > 0)
            //    {
            //        LastButtons = FretButtonType.None;

            //        foreach (var sustain in SustainNotes)
            //        {
            //            if (HitNext == true)
            //            {
            //                if (sustain.IsDead)
            //                    continue;

            //                if (sustain.Y - sustain.YScale <= -4.0f && sustain.Y + sustain.YScale >= -4.5f)
            //                {
            //                    if (sustain.NoteType.HasFlag(NoteType.Green))
            //                        LastButtons |= FretButtonType.Green | FretButtonType.Hit;

            //                    if (sustain.NoteType.HasFlag(NoteType.Red))
            //                        LastButtons |= FretButtonType.Red | FretButtonType.Hit;

            //                    if (sustain.NoteType.HasFlag(NoteType.Yellow))
            //                        LastButtons |= FretButtonType.Yellow | FretButtonType.Hit;

            //                    if (sustain.NoteType.HasFlag(NoteType.Blue))
            //                        LastButtons |= FretButtonType.Blue | FretButtonType.Hit;

            //                    if (sustain.NoteType.HasFlag(NoteType.Orange))
            //                        LastButtons |= FretButtonType.Orange | FretButtonType.Hit;

            //                    if (HitNext == false)
            //                        LastButtons &= ~FretButtonType.Hit;

            //                    sustainPressed = true;
            //                }
            //                else break;
            //            }
            //            else
            //            {
            //                if (sustain.IsDead && sustain.Y - sustain.YScale <= -4.0f && sustain.Y + sustain.YScale >= -4.5f)
            //                {
            //                    if (sustain.NoteType.HasFlag(NoteType.Green))
            //                        LastButtons |= FretButtonType.Green;

            //                    if (sustain.NoteType.HasFlag(NoteType.Red))
            //                        LastButtons |= FretButtonType.Red;

            //                    if (sustain.NoteType.HasFlag(NoteType.Yellow))
            //                        LastButtons |= FretButtonType.Yellow;

            //                    if (sustain.NoteType.HasFlag(NoteType.Blue))
            //                        LastButtons |= FretButtonType.Blue;

            //                    if (sustain.NoteType.HasFlag(NoteType.Orange))
            //                        LastButtons |= FretButtonType.Orange;

            //                    sustainPressed = true;
            //                }
            //                else break;
            //            }
            //        }

            //        if (LastButtons != FretButtonType.None && HitNext == true)
            //            EnableHitGlow(LastButtons);

            //        PressButtons(LastButtons);
            //    }

            //    #endregion

            //    if (!sustainPressed)
            //        hitRange = (4.5f * 500.0f) / 1000.0f;

            //    else hitRange = (4.5f * 150.0f) / 1000.0f;

            //    customHit = GetHitableNotes(hitRange);

            //    if (customHit.Count == 0 && !sustainPressed)
            //    {
            //        LastButtons = FretButtonType.None;

            //        if (HitNext == true)
            //            EnableHitGlow(LastButtons);

            //        PressButtons(LastButtons);
            //    }
            //    else
            //    {
            //        for (int i = 0; i < customHit.Count; i++)
            //        {
            //            var notes = customHit[i];
            //            LastButtons = FretButtonType.None;

            //            if (notes[0].IsDead)
            //                continue;

            //            foreach (var note in notes)
            //            {
            //                if (note.NoteType.HasFlag(NoteType.Green))
            //                    LastButtons |= FretButtonType.Green;

            //                if (note.NoteType.HasFlag(NoteType.Red))
            //                    LastButtons |= FretButtonType.Red;

            //                if (note.NoteType.HasFlag(NoteType.Yellow))
            //                    LastButtons |= FretButtonType.Yellow;

            //                if (note.NoteType.HasFlag(NoteType.Blue))
            //                    LastButtons |= FretButtonType.Blue;

            //                if (note.NoteType.HasFlag(NoteType.Orange))
            //                    LastButtons |= FretButtonType.Orange;
            //            }

            //            foreach (var sustain in SustainNotes)
            //            {
            //                if (sustain.Y - sustain.YScale <= -4.5f)
            //                {
            //                    sustain.NoteType |= NoteType.Dead;
            //                    EnableHitGlow(FretButtonType.None);
            //                }
            //            }

            //            PressButtons(LastButtons);
            //            break;
            //        }
            //    }
            //}

            #endregion

        }

        public float GetCurrentTime()
        {
            AudioEngine.Update(0.0f);
            return CurrentTime = AudioTrack.Playposition;
        }

        protected override void Draw()
        {
            FretboardEffect.Parameters["View"].SetValue(Camera.View);
            FretboardEffect.Parameters["Projection"].SetValue(Camera.Projection);

            SpriteEffect.Parameters["View"].SetValue(Camera.View);
            SpriteEffect.Parameters["Projection"].SetValue(Camera.Projection);

            EngineServices.GraphicsDevice.Viewport = Camera.Viewport;

            foreach (var pass in FretboardEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                Fretboard.Draw(FretboardEffect);
                CenterLines.Draw(FretboardEffect);
            }

            foreach (var pass in SpriteEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                foreach (var marker in BeatMarkers)
                    marker.Draw(SpriteEffect);
            }

            foreach (var pass in FretboardEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                Sidebars.Draw(FretboardEffect);

                for (int i = SustainNotes.Count - 1; i >= 0; i--)
                    SustainNotes[i].Draw(FretboardEffect);
            }


            foreach (var pass in SpriteEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                foreach (var button in Buttons)
                    button.Draw(SpriteEffect);

                for (int i = Notes.Count - 1; i >= 0; i--)
                {
                    if (Notes[i].Y >= -4.5f)
                        Notes[i].Draw(SpriteEffect);
                }
                for (int i = Hitglows.Count - 1; i >= 0; i--)
                    Hitglows[i].Draw(SpriteEffect);

                for (int i = Flames.Count - 1; i >= 0; i--)
                    Flames[i].Draw(SpriteEffect);

                for (int i = Notes.Count - 1; i >= 0; i--)
                {
                    if (Notes[i].Y <= -4.5f)
                        Notes[i].Draw(SpriteEffect);
                }
            }
        }

        protected override void Destroy()
        {

        }

        private bool GetIsInPowerSection(float noteTime)
        {
            if (CurrentNoteTrack == null)
                return false;

            if (LastPowerSection != null && noteTime >= LastPowerSection.Start && noteTime <= (LastPowerSection.Start + LastPowerSection.Length))
                return true;

            else
            {
                var powerSection = CurrentNoteTrack.PowerNoteSections;

                foreach (var section in powerSection)
                {
                    if (noteTime >= section.Start && noteTime <= (section.Start + section.Length))
                    {
                        if (CurrentPowerSection != section)
                        {
                            PowerModeEnabled = true;
                            LastPowerSection = CurrentPowerSection;
                        }

                        CurrentPowerSection = section;
                        return true;
                    }
                }
            }

            return false;
        }

        private bool GetIsInPlayerSection(float noteTime)
        {
            if (CurrentNoteTrack == null)
                return false;

            if (EnablePlayer1Notes == EnablePlayer2Notes)
                return true;

            var noteSection = EnablePlayer1Notes ? CurrentNoteTrack.Player1NoteSection : CurrentNoteTrack.Player2NoteSection;

            foreach (var section in noteSection)
            {
                if (noteTime >= section.Start && noteTime <= (section.Start + section.Length))
                    return true;
            }

            return false;
        }

        private bool GetIsLastNoteInPowerSection(float noteTime)
        {

            return false;
        }

        private bool GetIsFirstNoteInPlayerSection(NoteTrackEvent note)
        {
            if (CurrentNoteTrack == null || EnablePlayer1Notes == EnablePlayer2Notes)
                return false;

            var lastNote = GetFirstNoteBefore(note);

            if (lastNote == null)
                return false;

            return !GetIsInPlayerSection(lastNote.Start);
        }

        private NoteTrackEvent GetFirstNoteBefore(NoteTrackEvent note)
        {
            if (CurrentNoteTrack == null)
                return null;

            for (int i = CurrentNoteTrack.Count - 1; i >= 0; i--)
            {
                if (CurrentNoteTrack[i] == note)
                {
                    var currentIndex = i - 1;

                    while (currentIndex >= 0 && CurrentNoteTrack[currentIndex].Start == note.Start)
                        currentIndex--;

                    if (currentIndex >= 0)
                        return CurrentNoteTrack[currentIndex];

                    else return null;
                }
            }

            return null;
        }

        private void Strum()
        {
            var hitRange = (4.5f * 85.5f) / 1000.0f;
            var currentNotes = GetHitableNotes(hitRange);

            for (int i = 0; i < currentNotes.Count; i++)
            {
                if (CheckIsHit(currentNotes[i]))
                {
                    foreach (var note in currentNotes[i])
                    {
                        if ((note.NoteType & NoteType.Green) == NoteType.Green)
                            AddFlame(FretButtonType.Green);

                        if ((note.NoteType & NoteType.Red) == NoteType.Red)
                            AddFlame(FretButtonType.Red);

                        if ((note.NoteType & NoteType.Yellow) == NoteType.Yellow)
                            AddFlame(FretButtonType.Yellow);

                        if ((note.NoteType & NoteType.Blue) == NoteType.Blue)
                            AddFlame(FretButtonType.Blue);

                        if ((note.NoteType & NoteType.Orange) == NoteType.Orange)
                            AddFlame(FretButtonType.Orange);
                    }

                    RemoveNotes(currentNotes[i]);
                    AudioTrack.Volume = 50.0f;
                    return;
                }
                else if (i == 0)
                {
                    foreach (var note in currentNotes[i])
                        note.NoteType |= NoteType.Dead;

                    foreach (var sustain in SustainNotes)
                    {
                        if (sustain.Y - sustain.YScale <= -4.5f)
                        {
                            sustain.NoteType |= NoteType.Dead;
                            sustain.UpdateState(0.0f);
                        }
                    }
                }
                else break;
            }
        }

        private void SetupBoard()
        {
            var topTextureCoord = 0.0f;
            var bottomTextureCoord = 1.0f;
            var boardItemYScale = 5.0f;
            var boardItemXScale = 0.985f;

            var texture = Theme.GetTextureFromName("DefaultBoard").Texture;
            var centerLineTexture = Theme.GetTextureFromName("Centerlines").Texture;
            var sidebarTexture = Theme.GetTextureFromName("Sidebars").Texture;

            HitGlowBluePrint = Theme.GetAnimationFromName("Hitglow");

            Fretboard = new Fretboard(texture);
            CenterLines = new Fretboard(centerLineTexture);
            Sidebars = new Fretboard(sidebarTexture);

            Fretboard.TopTextureCoordinate = topTextureCoord;
            Fretboard.BottomTextureCoordinate = bottomTextureCoord;
            Fretboard.XScale = boardItemXScale;
            Fretboard.YScale = boardItemYScale;
            Fretboard.Initialize();

            CenterLines.TopTextureCoordinate = topTextureCoord;
            CenterLines.BottomTextureCoordinate = bottomTextureCoord;
            CenterLines.XScale = boardItemXScale;
            CenterLines.YScale = boardItemYScale;
            CenterLines.Initialize();

            Sidebars.TopTextureCoordinate = topTextureCoord;
            Sidebars.BottomTextureCoordinate = bottomTextureCoord;
            Sidebars.XScale = boardItemXScale * 1.03f;
            Sidebars.YScale = boardItemYScale;
            Sidebars.Initialize();

            AddButton(FretButtonType.Green);
            AddButton(FretButtonType.Red);
            AddButton(FretButtonType.Yellow);
            AddButton(FretButtonType.Blue);
            AddButton(FretButtonType.Orange);

            AddHitGlow(FretButtonType.Green);
            AddHitGlow(FretButtonType.Red);
            AddHitGlow(FretButtonType.Yellow);
            AddHitGlow(FretButtonType.Blue);
            AddHitGlow(FretButtonType.Orange);
        }

        private void SetupCamera()
        {
            var positionY = 0;
            var playerIndex = (int)PlayerIndex;
            var playerCount = 1;

            var width = EngineServices.ClientWidth;
            var height = EngineServices.ClientHeight;
            var clientWidth = EngineServices.ClientWidth;
            width = (int)(width * .45f);

            var position = (float)playerIndex * width;
            var positionOffset = ((1 - (width / (clientWidth / (float)playerCount))) * clientWidth) * 0.5f;

            if (playerCount > 1)
            {
                var halfOffset = positionOffset * 0.5f;
                positionOffset += (-halfOffset + (halfOffset * playerIndex));
            }

            position += positionOffset;

            Camera.Viewport = new Viewport(new Rectangle((int)position, positionY, width, height - positionY));

            Camera.AspectRatio = 1.092f;
            Camera.FieldOfView = 30.0f;
            Camera.Y = -8.3f;
            Camera.Z = 1.4f;
            Camera.XRotation = 82.0f;
            Camera.FarPlane = 13.5f;
        }

        private void SetupNoteContent()
        {
            GreenNote = Theme.GetTextureFromName("GreenNote");
            GreenHopoNote = Theme.GetTextureFromName("GreenHopoNote");
            GreenTapoNote = Theme.GetTextureFromName("GreenTapoNote");
            GreenPowerNote = Theme.GetTextureFromName("GreenPowerNote");
            GreenPowerHopoNote = Theme.GetTextureFromName("GreenPowerHopoNote");
            GreenPowerTapoNote = Theme.GetTextureFromName("GreenPowerTapoNote");

            RedNote = Theme.GetTextureFromName("RedNote");
            RedHopoNote = Theme.GetTextureFromName("RedHopoNote");
            RedTapoNote = Theme.GetTextureFromName("RedTapoNote");
            RedPowerNote = Theme.GetTextureFromName("RedPowerNote");
            RedPowerHopoNote = Theme.GetTextureFromName("RedPowerHopoNote");
            RedPowerTapoNote = Theme.GetTextureFromName("RedPowerTapoNote");

            YellowNote = Theme.GetTextureFromName("YellowNote");
            YellowHopoNote = Theme.GetTextureFromName("YellowHopoNote");
            YellowTapoNote = Theme.GetTextureFromName("YellowTapoNote");
            YellowPowerNote = Theme.GetTextureFromName("YellowPowerNote");
            YellowPowerHopoNote = Theme.GetTextureFromName("YellowPowerHopoNote");
            YellowPowerTapoNote = Theme.GetTextureFromName("YellowPowerTapoNote");

            BlueNote = Theme.GetTextureFromName("BlueNote");
            BlueHopoNote = Theme.GetTextureFromName("BlueHopoNote");
            BlueTapoNote = Theme.GetTextureFromName("BlueTapoNote");
            BluePowerNote = Theme.GetTextureFromName("BluePowerNote");
            BluePowerHopoNote = Theme.GetTextureFromName("BluePowerHopoNote");
            BluePowerTapoNote = Theme.GetTextureFromName("BluePowerTapoNote");

            OrangeNote = Theme.GetTextureFromName("OrangeNote");
            OrangeHopoNote = Theme.GetTextureFromName("OrangeHopoNote");
            OrangeTapoNote = Theme.GetTextureFromName("OrangeTapoNote");
            OrangePowerNote = Theme.GetTextureFromName("OrangePowerNote");
            OrangePowerHopoNote = Theme.GetTextureFromName("OrangePowerHopoNote");
            OrangePowerTapoNote = Theme.GetTextureFromName("OrangePowerTapoNote");

            DeadNote = Theme.GetTextureFromName("DeadNote");
            DeadHopoNote = Theme.GetTextureFromName("DeadHopoNote");
            DeadTapoNote = Theme.GetTextureFromName("DeadTapoNote");
            DeadPowerNote = Theme.GetTextureFromName("DeadPowerNote");
            DeadPowerHopoNote = Theme.GetTextureFromName("DeadPowerHopoNote");
            DeadPowerTapoNote = Theme.GetTextureFromName("DeadPowerTapoNote");

            OverdriveNote = Theme.GetTextureFromName("OverdriveNote");
            OverdriveHopoNote = Theme.GetTextureFromName("OverdriveHopoNote");
            OverdriveTapoNote = Theme.GetTextureFromName("OverdriveTapoNote");
            OverdrivePowerNote = Theme.GetTextureFromName("OverdrivePowerNote");
            OverdrivePowerHopoNote = Theme.GetTextureFromName("OverdrivePowerHopoNote");
            OverdrivePowerTapoNote = Theme.GetTextureFromName("OverdrivePowerTapoNote");



            GreenSustain = Theme.GetTextureFromName("GreenSustain");
            GreenSustainActive = Theme.GetTextureFromName("GreenSustainActive");

            RedSustain = Theme.GetTextureFromName("RedSustain");
            RedSustainActive = Theme.GetTextureFromName("RedSustainActive");

            YellowSustain = Theme.GetTextureFromName("YellowSustain");
            YellowSustainActive = Theme.GetTextureFromName("YellowSustainActive");

            BlueSustain = Theme.GetTextureFromName("BlueSustain");
            BlueSustainActive = Theme.GetTextureFromName("BlueSustainActive");

            OrangeSustain = Theme.GetTextureFromName("OrangeSustain");
            OrangeSustainActive = Theme.GetTextureFromName("OrangeSustainActive");

            DeadSustain = Theme.GetTextureFromName("DeadSustain");
            PowerSustain = Theme.GetTextureFromName("PowerSustainActive");
            PowerSustainActive = Theme.GetTextureFromName("PowerSustain");

            OverdriveSustain = Theme.GetTextureFromName("OverdriveSustain");
            OverdriveSustainActive = Theme.GetTextureFromName("OverdriveSustainActive");
        }

        private void AddButton(FretButtonType buttonType)
        {
            var button = new Fretbutton(buttonType);
            var isGreen = (button.ButtonType & FretButtonType.Green) == FretButtonType.Green;
            var isRed = (button.ButtonType & FretButtonType.Red) == FretButtonType.Red;
            var isYellow = (button.ButtonType & FretButtonType.Yellow) == FretButtonType.Yellow;
            var isBlue = (button.ButtonType & FretButtonType.Blue) == FretButtonType.Blue;
            var isOrange = (button.ButtonType & FretButtonType.Orange) == FretButtonType.Orange;

            var scale = 0.133f;

            button.Y = -4.5f;
            button.YScale = scale;
            button.Z = scale / 2.0f;
            button.XScale = 0.1825f;
            button.XRotation = 10.0f;

            if (isGreen)
            {
                button.X = -0.755f;

                button.NormalTexture = Theme.GetTextureFromName("GreenFret");
                button.HitTexture = Theme.GetTextureFromName("GreenFretHit");
                button.PressedTexture = Theme.GetTextureFromName("GreenFretPressed");
            }
            else if (isRed)
            {
                button.X = -0.375f;
                button.NormalTexture = Theme.GetTextureFromName("RedFret");
                button.HitTexture = Theme.GetTextureFromName("RedFretHit");
                button.PressedTexture = Theme.GetTextureFromName("RedFretPressed");
            }
            else if (isYellow)
            {
                button.NormalTexture = Theme.GetTextureFromName("YellowFret");
                button.HitTexture = Theme.GetTextureFromName("YellowFretHit");
                button.PressedTexture = Theme.GetTextureFromName("YellowFretPressed");
            }
            else if (isBlue)
            {
                button.X = 0.38f;
                button.NormalTexture = Theme.GetTextureFromName("BlueFret");
                button.HitTexture = Theme.GetTextureFromName("BlueFretHit");
                button.PressedTexture = Theme.GetTextureFromName("BlueFretPressed");
            }
            else if (isOrange)
            {
                button.X = 0.76f;
                button.NormalTexture = Theme.GetTextureFromName("OrangeFret");
                button.HitTexture = Theme.GetTextureFromName("OrangeFretHit");
                button.PressedTexture = Theme.GetTextureFromName("OrangeFretPressed");
            }

            Buttons.Add(button);
        }

        private void AddHitGlow(FretButtonType buttonType)
        {
            var hitGlow = new AnimatedSprite();
            hitGlow.Texture = HitGlowBluePrint.Texture;
            hitGlow.FrameRate = HitGlowBluePrint.FrameRate;

            foreach (var frame in HitGlowBluePrint.Frames)
                hitGlow.Frames.Add(frame);

            hitGlow.EnableDrawing = false;
            hitGlow.Y = -4.0f;
            hitGlow.YScale = 0.25f;
            hitGlow.XScale = 0.1875f;
            hitGlow.Z = hitGlow.YScale / (4.0f * (hitGlow.XScale / hitGlow.YScale));
            hitGlow.XRotation = 45.0f;

            var aspect = hitGlow.XScale / hitGlow.YScale;

            if (buttonType.HasFlag(FretButtonType.Green))
                hitGlow.X = -0.82f;

            if (buttonType.HasFlag(FretButtonType.Red))
                hitGlow.X = -0.41f;

            if (buttonType.HasFlag(FretButtonType.Blue))
                hitGlow.X = 0.425f;

            if (buttonType.HasFlag(FretButtonType.Orange))
                hitGlow.X = 0.85f;

            Hitglows.Add(hitGlow);
        }

        private void AddBeatMarker(MarkerType markerType, float offset)
        {
            // Because it would take to long to try and retrive the texture for the beat marker every time the maker
            // needs to be added, lets store a local copy
            var beat = new Beatmarker(markerType, BeatMarkerTexture);
            beat.Alpha = 0.0f;
            beat.XScale = 0.995f;
            beat.XRotation = Camera.XRotation;

            BeatMarkers.Add(beat);
            beat.Y = (5.025f * ((AudioDelay * BoardSpeed) - AudioDelay + 1)) - offset;

            switch (markerType)
            {
                case MarkerType.Beat:
                    beat.YScale = .0066f;
                    break;

                case MarkerType.Eigth:
                    beat.YScale = .005f;
                    break;

                case MarkerType.Measure:
                    beat.YScale = .01f;
                    break;
            }
            beat.Z = -beat.YScale / 2.0f;
            beat.UpdateState(0.0f);
        }

        private void AddNote(NoteType noteType, float offset)
        {
            var note = new PlayNote(noteType);
            var isHopo = note.IsHopo;
            var isTapo = note.IsTapo;

            var swapValue = NotesSwapped ? -1.0f : 1.0f;
            var isGreen = (note.NoteType & NoteType.Green) == NoteType.Green;
            var isRed = (note.NoteType & NoteType.Red) == NoteType.Red;
            var isYellow = (note.NoteType & NoteType.Yellow) == NoteType.Yellow;
            var isBlue = (note.NoteType & NoteType.Blue) == NoteType.Blue;
            var isOrange = (note.NoteType & NoteType.Orange) == NoteType.Orange;

            note.Alpha = 0.0f;
            note.YScale = 0.1875f;
            note.XScale = 0.1875f;
            note.Y = (5.0f * ((AudioDelay * BoardSpeed) - AudioDelay + 1)) - offset;
            note.Z = note.YScale / (4.0f * (note.YScale / note.XScale));
            note.XRotation = 45.0f;

            note.DeadTexture = DeadNote;
            note.DeadHopoTexture = DeadHopoNote;
            note.DeadTapoTexture = DeadTapoNote;
            note.DeadPowerTexture = DeadPowerNote;
            note.DeadPowerHopoTexture = DeadPowerHopoNote;
            note.DeadPowerTapoTexture = DeadPowerTapoNote;

            note.OverdriveTexture = OverdriveNote;
            note.OverdriveHopoTexture = OverdriveHopoNote;
            note.OverdriveTapoTexture = OverdriveTapoNote;
            note.OverdrivePowerTexture = OverdrivePowerNote;
            note.OverdrivePowerHopoTexture = OverdrivePowerHopoNote;
            note.OverdrivePowerTapoTexture = OverdrivePowerTapoNote;

            Notes.Add(note);

            #region Green Note Setup

            if (isGreen)
            {
                note.X = -0.775f * swapValue;

                note.NormalTexture = GreenNote;
                note.HopoTexture = GreenHopoNote;
                note.TapoTexture = GreenTapoNote;
                note.PowerTexture = GreenPowerNote;
                note.PowerHopoTexture = GreenPowerHopoNote;
                note.PowerTapoTexture = GreenPowerTapoNote;
            }

            #endregion

            #region Red Note Setup

            else if (isRed)
            {
                note.X = -0.387f * swapValue;

                note.NormalTexture = RedNote;
                note.HopoTexture = RedHopoNote;
                note.TapoTexture = RedTapoNote;
                note.PowerTexture = RedPowerNote;
                note.PowerHopoTexture = RedPowerHopoNote;
                note.PowerTapoTexture = RedPowerTapoNote;
            }

            #endregion

            #region Yellow Note Setup

            else if (isYellow)
            {
                note.NormalTexture = YellowNote;
                note.HopoTexture = YellowHopoNote;
                note.TapoTexture = YellowTapoNote;
                note.PowerTexture = YellowPowerNote;
                note.PowerHopoTexture = YellowPowerHopoNote;
                note.PowerTapoTexture = YellowPowerTapoNote;
            }

            #endregion

            #region Blue Note Setup

            else if (isBlue)
            {
                note.X = 0.387f * swapValue;

                note.NormalTexture = BlueNote;
                note.HopoTexture = BlueHopoNote;
                note.TapoTexture = BlueTapoNote;
                note.PowerTexture = BluePowerNote;
                note.PowerHopoTexture = BluePowerHopoNote;
                note.PowerTapoTexture = BluePowerTapoNote;
            }

            #endregion

            #region Orange Note Setup

            else if (isOrange)
            {
                note.X = 0.775f * swapValue;

                note.NormalTexture = OrangeNote;
                note.HopoTexture = OrangeHopoNote;
                note.TapoTexture = OrangeTapoNote;
                note.PowerTexture = OrangePowerNote;
                note.PowerHopoTexture = OrangePowerHopoNote;
                note.PowerTapoTexture = OrangePowerTapoNote;
            }

            #endregion

            note.UpdateState(0.0f);
        }

        private void AddSustain(NoteType noteType, float length, float delay, float offset)
        {
            var note = new Sustain(noteType, length, (delay * 5 * BoardSpeed * (Playspeed / 100.0f)));

            var swapValue = NotesSwapped ? -1.0f : 1.0f;
            var isGreen = (note.NoteType & NoteType.Green) == NoteType.Green;
            var isRed = (note.NoteType & NoteType.Red) == NoteType.Red;
            var isYellow = (note.NoteType & NoteType.Yellow) == NoteType.Yellow;
            var isBlue = (note.NoteType & NoteType.Blue) == NoteType.Blue;
            var isOrange = (note.NoteType & NoteType.Orange) == NoteType.Orange;

            note.Alpha = 0.0f;
            note.XScale = 0.1875f;
            note.Y = (5.0f * ((AudioDelay * BoardSpeed) - AudioDelay + 1)) - offset;
            note.YScale = offset / 2.0f;
            note.DeadTexture = DeadSustain;
            note.PowerTexture = PowerSustain;
            note.OverdriveTexture = OverdriveSustain;

            if (isGreen)
            {
                note.X = -0.775f * swapValue;
                note.NormalTexture = GreenSustainActive;
            }

            else if (isRed)
            {
                note.X = -0.387f * swapValue;
                note.NormalTexture = RedSustainActive;
            }

            else if (isYellow)
                note.NormalTexture = YellowSustainActive;

            else if (isBlue)
            {
                note.X = 0.387f * swapValue;
                note.NormalTexture = BlueSustainActive;
            }

            else if (isOrange)
            {
                note.X = 0.775f * swapValue;
                note.NormalTexture = OrangeSustainActive;
            }

            SustainNotes.Add(note);
            note.Initialize();
        }

        public void AddFlame(FretButtonType type)
        {
            var flame = new Fretbutton(type);
            var isGreen = (flame.ButtonType & FretButtonType.Green) == FretButtonType.Green;
            var isRed = (flame.ButtonType & FretButtonType.Red) == FretButtonType.Red;
            var isYellow = (flame.ButtonType & FretButtonType.Yellow) == FretButtonType.Yellow;
            var isBlue = (flame.ButtonType & FretButtonType.Blue) == FretButtonType.Blue;
            var isOrange = (flame.ButtonType & FretButtonType.Orange) == FretButtonType.Orange;

            var scale = 0.133f;

            flame.Alpha = .5f;
            flame.Y = -4.3f;
            flame.YScale = scale;
            flame.Z = scale / 2.0f;
            flame.XScale = 0.1825f;
            flame.XRotation = Camera.XRotation;
            flame.NormalTexture = Theme.GetTextureFromName("Flame");

            if (isGreen)
                flame.X = -0.8f;

            else if (isRed)
                flame.X = -0.4f;

            else if (isBlue)
                flame.X = 0.4125f;

            else if (isOrange)
                flame.X = 0.825f;

            Flames.Add(flame);
            flame.UpdateState(0.0f);
        }

        private void InitializeButton(FretButtonType buttonType, float delta)
        {
            var isGreen = (buttonType & FretButtonType.Green) == FretButtonType.Green;
            var isRed = (buttonType & FretButtonType.Red) == FretButtonType.Red;
            var isYellow = (buttonType & FretButtonType.Yellow) == FretButtonType.Yellow;
            var isBlue = (buttonType & FretButtonType.Blue) == FretButtonType.Blue;
            var isOrange = (buttonType & FretButtonType.Orange) == FretButtonType.Orange;

            ResetButtonStates();

            foreach (var button in Buttons)
            {
                if (isGreen && (button.ButtonType & FretButtonType.Green) == FretButtonType.Green)
                    button.ButtonType |= FretButtonType.Pressed;

                else if (isRed && (button.ButtonType & FretButtonType.Red) == FretButtonType.Red)
                    button.ButtonType |= FretButtonType.Pressed;

                else if (isYellow && (button.ButtonType & FretButtonType.Yellow) == FretButtonType.Yellow)
                    button.ButtonType |= FretButtonType.Pressed;

                else if (isBlue && (button.ButtonType & FretButtonType.Blue) == FretButtonType.Blue)
                    button.ButtonType |= FretButtonType.Pressed;

                else if (isOrange && (button.ButtonType & FretButtonType.Orange) == FretButtonType.Orange)
                    button.ButtonType |= FretButtonType.Pressed;
            }

            ButtonInitializationTimeRemaining = delta;
        }

        private void ResetButtonStates()
        {
            foreach (var button in Buttons)
            {
                if (button.ButtonType.HasFlag(FretButtonType.Pressed))
                    button.ButtonType &= ~FretButtonType.Pressed;

                if (button.ButtonType.HasFlag(FretButtonType.Hit))
                    button.ButtonType &= ~FretButtonType.Hit;
            }
        }

        private bool CheckIsHit(List<PlayNote> notes)
        {
            var isGreen = false;
            var isRed = false;
            var isYellow = false;
            var isBlue = false;
            var isOrange = false;
            var buttons = FretButtonType.None;

            #region SetButtons

            foreach (var noteType in notes)
            {
                isGreen = (noteType.NoteType & NoteType.Green) == NoteType.Green;
                isRed = (noteType.NoteType & NoteType.Red) == NoteType.Red;
                isYellow = (noteType.NoteType & NoteType.Yellow) == NoteType.Yellow;
                isBlue = (noteType.NoteType & NoteType.Blue) == NoteType.Blue;
                isOrange = (noteType.NoteType & NoteType.Orange) == NoteType.Orange;

                if (isGreen)
                    buttons |= FretButtonType.Green;

                if (isRed)
                    buttons |= FretButtonType.Red;

                if (isYellow)
                    buttons |= FretButtonType.Yellow;

                if (isBlue)
                    buttons |= FretButtonType.Blue;

                if (isOrange)
                    buttons |= FretButtonType.Orange;
            }

            #endregion

            #region Lucky Break

            // Cool we got a good match, no other checks are needed
            if (buttons == LastButtons)
                return true;

            #endregion

            #region Check notes the hard way

            var lastIsGreen = (LastButtons & FretButtonType.Green) == FretButtonType.Green;
            var lastIsRed = (LastButtons & FretButtonType.Red) == FretButtonType.Red;
            var lastIsYellow = (LastButtons & FretButtonType.Yellow) == FretButtonType.Yellow;
            var lastIsBlue = (LastButtons & FretButtonType.Blue) == FretButtonType.Blue;
            var lastIsOrange = (LastButtons & FretButtonType.Orange) == FretButtonType.Orange;

            // since on the guitar, you can press buttons on a lower level, without effecting the high level notes, 
            // lets get check in order of complexity

            #region Check Green Button

            if (isGreen && isRed && isYellow && isBlue && isOrange)
            {
                if (lastIsGreen && lastIsRed && lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isYellow && isBlue && isOrange)
            {
                if (lastIsGreen && !lastIsRed && lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isBlue && isOrange)
            {
                if (lastIsGreen && lastIsRed && !lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isYellow && isOrange)
            {
                if (lastIsGreen && lastIsRed && lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isYellow && isBlue)
            {
                if (lastIsGreen && lastIsRed && lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isOrange)
            {
                if (lastIsGreen && lastIsRed && !lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isBlue)
            {
                if (lastIsGreen && lastIsRed && !lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed && isYellow)
            {
                if (lastIsGreen && lastIsRed && lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isYellow && isBlue)
            {
                if (lastIsGreen && !lastIsRed && lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isYellow && isOrange)
            {
                if (lastIsGreen && !lastIsRed && lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isBlue && isOrange)
            {
                if (lastIsGreen && !lastIsRed && !lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isOrange)
            {
                if (lastIsGreen && !lastIsRed && !lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isGreen && isBlue)
            {
                if (lastIsGreen && !lastIsRed && !lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isYellow)
            {
                if (lastIsGreen && !lastIsRed && lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen && isRed)
            {
                if (lastIsGreen && lastIsRed && !lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isGreen)
            {
                if (lastIsGreen && !lastIsRed && !lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }

            #endregion

            #region Check Red Button

            else if (isRed && isYellow && isBlue && isOrange)
            {
                if (lastIsRed && lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isRed && isYellow && isOrange)
            {
                if (lastIsRed && lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isRed && isYellow && isBlue)
            {
                if (lastIsRed && lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isRed && isBlue && isOrange)
            {
                if (lastIsRed && !lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isRed && isOrange)
            {
                if (lastIsRed && !lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isRed && isBlue)
            {
                if (lastIsRed && !lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isRed && isYellow)
            {
                if (lastIsRed && lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isRed)
            {
                if (lastIsRed && !lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }

            #endregion

            #region Check Yellow Button

            else if (isYellow && isBlue && isOrange)
            {
                if (lastIsYellow && lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isYellow && isOrange)
            {
                if (lastIsYellow && !lastIsBlue && lastIsOrange)
                    return true;
            }
            else if (isYellow && isBlue)
            {
                if (lastIsYellow && lastIsBlue && !lastIsOrange)
                    return true;
            }
            else if (isYellow)
            {
                if (lastIsYellow && !lastIsBlue && !lastIsOrange)
                    return true;
            }

            #endregion

            #region Check Blue Button

            else if (isBlue && isOrange)
            {
                if (lastIsBlue && lastIsOrange)
                    return true;
            }

            else if (isBlue)
            {
                if (lastIsBlue && !lastIsOrange)
                    return true;
            }

            #endregion

            #region Check Orange Button

            else if (isOrange)
            {
                if (lastIsOrange)
                    return true;
            }

            #endregion

            #endregion

            return false;
        }

        private void PressButtons(FretButtonType button)
        {
            var isGreen = (button & FretButtonType.Green) == FretButtonType.Green;
            var isRed = (button & FretButtonType.Red) == FretButtonType.Red;
            var isYellow = (button & FretButtonType.Yellow) == FretButtonType.Yellow;
            var isBlue = (button & FretButtonType.Blue) == FretButtonType.Blue;
            var isOrange = (button & FretButtonType.Orange) == FretButtonType.Orange;

            foreach (var fret in Buttons)
            {
                if ((fret.ButtonType & FretButtonType.Green) == FretButtonType.Green)
                {
                    if (isGreen)
                    {
                        if (button.HasFlag(FretButtonType.Hit))
                            fret.ButtonType |= FretButtonType.Hit;

                        fret.ButtonType |= FretButtonType.Pressed;
                    }

                    else
                    {
                        fret.ButtonType &= ~FretButtonType.Hit;
                        fret.ButtonType &= ~FretButtonType.Pressed;
                    }
                }

                if ((fret.ButtonType & FretButtonType.Red) == FretButtonType.Red)
                {
                    if (isRed)
                    {
                        if (button.HasFlag(FretButtonType.Hit))
                            fret.ButtonType |= FretButtonType.Hit;

                        fret.ButtonType |= FretButtonType.Pressed;
                    }

                    else
                    {
                        fret.ButtonType &= ~FretButtonType.Hit;
                        fret.ButtonType &= ~FretButtonType.Pressed;
                    }
                }

                if ((fret.ButtonType & FretButtonType.Yellow) == FretButtonType.Yellow)
                {
                    if (isYellow)
                    {
                        if (button.HasFlag(FretButtonType.Hit))
                            fret.ButtonType |= FretButtonType.Hit;

                        fret.ButtonType |= FretButtonType.Pressed;
                    }

                    else
                    {
                        fret.ButtonType &= ~FretButtonType.Hit;
                        fret.ButtonType &= ~FretButtonType.Pressed;
                    }
                }

                if ((fret.ButtonType & FretButtonType.Blue) == FretButtonType.Blue)
                {
                    if (isBlue)
                    {
                        if (button.HasFlag(FretButtonType.Hit))
                            fret.ButtonType |= FretButtonType.Hit;

                        fret.ButtonType |= FretButtonType.Pressed;
                    }

                    else
                    {
                        fret.ButtonType &= ~FretButtonType.Hit;
                        fret.ButtonType &= ~FretButtonType.Pressed;
                    }
                }

                if ((fret.ButtonType & FretButtonType.Orange) == FretButtonType.Orange)
                {
                    if (isOrange)
                    {
                        if (button.HasFlag(FretButtonType.Hit))
                            fret.ButtonType |= FretButtonType.Hit;

                        fret.ButtonType |= FretButtonType.Pressed;
                    }

                    else
                    {
                        fret.ButtonType &= ~FretButtonType.Hit;
                        fret.ButtonType &= ~FretButtonType.Pressed;
                    }
                }
            }

            LastButtons = button;
        }

        private void EnableHitGlow(FretButtonType button)
        {
            foreach (var glow in Hitglows)
                glow.EnableDrawing = false;

            for (int i = 0; i < Hitglows.Count; i++)
            {
                var hitGlow = Hitglows[i];

                if (button.HasFlag(FretButtonType.Green) && i == 0)
                    hitGlow.EnableDrawing = true;

                if (button.HasFlag(FretButtonType.Red) && i == 1)
                    hitGlow.EnableDrawing = true;

                if (button.HasFlag(FretButtonType.Yellow) && i == 2)
                    hitGlow.EnableDrawing = true;

                if (button.HasFlag(FretButtonType.Blue) && i == 3)
                    hitGlow.EnableDrawing = true;

                if (button.HasFlag(FretButtonType.Orange) && i == 4)
                    hitGlow.EnableDrawing = true;
            }
        }

        private void ResetPowerNotes()
        {
            foreach (var note in Notes)
            {
                if (note.NoteType.HasFlag(NoteType.Power))
                    note.NoteType &= ~NoteType.Power;
            }

            PowerModeEnabled = false;
            LastPowerSection = CurrentPowerSection;
        }

        private void RemoveNotes(List<PlayNote> notes)
        {
            foreach (var notetype in notes)
                Notes.Remove(notetype);
        }

        private void UpdateInitialization(float delta)
        {
            if (!IsBoardInPosition)
            {
                BoardPositionTimeRemaing -= delta;

                if (BoardPositionTimeRemaing < 0.0f)
                {
                    IsBoardInPosition = true;
                    Camera.XRotation = FinalBoardPosition;
                    ButtonInitializationTimeRemaining = ButtonInitializationTime;
                    InitializeButton(FretButtonType.Green, ButtonInitializationTime);
                }
                else Camera.XRotation -= ((Camera.XRotation - FinalBoardPosition) / BoardPositionTimeRemaing) / 60.0f;
            }
            else if (!float.IsNaN(ButtonInitializationTimeRemaining))
            {
                ButtonInitializationTimeRemaining -= delta;

                if (ButtonInitializationTimeRemaining <= 0)
                {
                    if (!IsGreenButtonInitialized)
                    {
                        IsGreenButtonInitialized = true;
                        InitializeButton(FretButtonType.Red, ButtonInitializationTime);
                    }
                    else if (!IsRedButtonInitailzed)
                    {
                        IsRedButtonInitailzed = true;
                        InitializeButton(FretButtonType.Yellow, ButtonInitializationTime);
                    }
                    else if (!IsYellowButtonInitalized)
                    {
                        IsYellowButtonInitalized = true;
                        InitializeButton(FretButtonType.Blue, ButtonInitializationTime);
                    }
                    else if (!IsBlueButtonInitialized)
                    {
                        IsBlueButtonInitialized = true;
                        InitializeButton(FretButtonType.Orange, ButtonInitializationTime);
                    }
                    else if (FullButtonIntialiationCount < 4)
                    {
                        if (FullButtonIntialiationCount % 2 == 0)
                            InitializeButton(FretButtonType.None, ButtonInitializationTime);

                        else InitializeButton(FretButtonType.Green | FretButtonType.Red | FretButtonType.Yellow | FretButtonType.Blue | FretButtonType.Orange, ButtonInitializationTime);

                        FullButtonIntialiationCount++;
                    }
                    else
                    {
                        AreButtonsInitialized = true;
                        ResetButtonStates();
                    }
                }
            }
        }

        private List<List<PlayNote>> GetHitableNotes(float hitRange)
        {
            var hitStart = Buttons[0].Y + hitRange;
            var hitEnd = Buttons[0].Y - hitRange;

            var lastPlayNote = new List<PlayNote>();
            var currentNotes = new List<List<PlayNote>>();

            #region Get Current Hitable Notes

            for (int i = 0; i < Notes.Count; i++)
            {
                var note = Notes[i];

                if (!note.IsDead)
                {
                    if (note.Y > hitEnd && note.Y < hitStart)
                    {
                        if (lastPlayNote.Count == 0)
                            lastPlayNote.Add(note);
                        else
                        {
                            if (lastPlayNote[0].Y == note.Y)
                                lastPlayNote.Add(note);
                            else
                            {
                                currentNotes.Add(new List<PlayNote>(lastPlayNote));
                                lastPlayNote.Clear();
                                lastPlayNote.Add(note);
                            }
                        }
                    }
                    else if (note.Y < hitEnd)
                        note.NoteType |= NoteType.Dead;

                    else if (note.Y > hitStart)
                        break;
                }
                else if (note.Y > hitStart)
                    break;
            }

            if (lastPlayNote.Count > 0)
                currentNotes.Add(lastPlayNote);

            #endregion

            return currentNotes;
        }

        #endregion

        private bool IsBoardInPosition = false;
        private bool IsGreenButtonInitialized = false;
        private bool IsRedButtonInitailzed = false;
        private bool IsYellowButtonInitalized = false;
        private bool IsBlueButtonInitialized = false;
        private bool AreButtonsInitialized = false;

        private int FullButtonIntialiationCount;
        private int CurrentTimeSigNumerator;
        private int CurrentTimeSigDenominator;
        private int LastBeatIndex;
        private int LastNoteIndex;
        private int LastTimeSigIndex;

        private float ButtonInitializationTime = 0.1f;
        private float ButtonInitializationTimeRemaining = 0.0f;

        private float BoardRate;
        private float FinalBoardPosition;
        private float BoardPositionTimeRemaing;

        private bool LastNoteHit = false;
        private bool NotesSwapped;
        private bool PowerModeEnabled;
        private bool EnablePlayer1Notes;
        private bool EnablePlayer2Notes;

        private Section LastPowerSection;
        private Section CurrentPowerSection;

        private float Playspeed;
        private float BoardSpeed;
        private float CurrentTime;
        private float AudioDelay;

        private float CurrentBPM;

        private AudioTrack AudioTrack;
        private PlayerType PlayerType;

        private FretButtonType LastButtons;

        private Fretboard Sidebars;
        private Fretboard Fretboard;
        private Fretboard CenterLines;
        private NoteTrack CurrentNoteTrack;
        private Chart ChartFile;
        private Random BotRandom;

        private Texture2D BeatMarkerTexture;

        private List<PlayNote> Notes;
        private List<Sustain> SustainNotes;
        private List<Fretbutton> Flames;
        private List<AnimatedSprite> Hitglows;

        private List<Fretbutton> Buttons;
        private List<Beatmarker> BeatMarkers;

        #region Note Textures

        TextureInfo GreenNote;
        TextureInfo GreenHopoNote;
        TextureInfo GreenTapoNote;
        TextureInfo GreenPowerNote;
        TextureInfo GreenPowerHopoNote;
        TextureInfo GreenPowerTapoNote;

        TextureInfo GreenSustain;
        TextureInfo GreenSustainActive;

        TextureInfo RedNote;
        TextureInfo RedHopoNote;
        TextureInfo RedTapoNote;
        TextureInfo RedPowerNote;
        TextureInfo RedPowerHopoNote;
        TextureInfo RedPowerTapoNote;

        TextureInfo RedSustain;
        TextureInfo RedSustainActive;

        TextureInfo YellowNote;
        TextureInfo YellowHopoNote;
        TextureInfo YellowTapoNote;
        TextureInfo YellowPowerNote;
        TextureInfo YellowPowerHopoNote;
        TextureInfo YellowPowerTapoNote;

        TextureInfo YellowSustain;
        TextureInfo YellowSustainActive;

        TextureInfo BlueNote;
        TextureInfo BlueHopoNote;
        TextureInfo BlueTapoNote;
        TextureInfo BluePowerNote;
        TextureInfo BluePowerHopoNote;
        TextureInfo BluePowerTapoNote;

        TextureInfo BlueSustain;
        TextureInfo BlueSustainActive;

        TextureInfo OrangeNote;
        TextureInfo OrangeHopoNote;
        TextureInfo OrangeTapoNote;
        TextureInfo OrangePowerNote;
        TextureInfo OrangePowerHopoNote;
        TextureInfo OrangePowerTapoNote;

        TextureInfo OrangeSustain;
        TextureInfo OrangeSustainActive;

        TextureInfo DeadNote;
        TextureInfo DeadHopoNote;
        TextureInfo DeadTapoNote;
        TextureInfo DeadPowerNote;
        TextureInfo DeadPowerHopoNote;
        TextureInfo DeadPowerTapoNote;

        TextureInfo OverdriveNote;
        TextureInfo OverdriveHopoNote;
        TextureInfo OverdriveTapoNote;
        TextureInfo OverdrivePowerNote;
        TextureInfo OverdrivePowerHopoNote;
        TextureInfo OverdrivePowerTapoNote;

        TextureInfo DeadSustain;
        TextureInfo PowerSustain;
        TextureInfo OverdriveSustain;
        TextureInfo PowerSustainActive;
        TextureInfo OverdriveSustainActive;

        AnimatedSprite HitGlowBluePrint;

        #endregion
    }
}
